View Javadoc
1   /**
2    * 
3    */
4   package edu.jiangxin.apktoolbox.android.i18n;
5   
6   import edu.jiangxin.apktoolbox.swing.extend.EasyPanel;
7   import edu.jiangxin.apktoolbox.swing.extend.listener.SelectDirectoryListener;
8   import edu.jiangxin.apktoolbox.utils.Constants;
9   import org.apache.commons.collections4.map.HashedMap;
10  import org.apache.commons.io.FileUtils;
11  import org.apache.commons.lang3.StringUtils;
12  import org.apache.commons.lang3.Strings;
13  import org.jdom2.Document;
14  import org.jdom2.Element;
15  import org.jdom2.JDOMException;
16  import org.jdom2.input.SAXBuilder;
17  import org.jdom2.output.Format;
18  import org.jdom2.output.XMLOutputter;
19  
20  import javax.swing.*;
21  import java.awt.*;
22  import java.io.*;
23  import java.util.ArrayList;
24  import java.util.Arrays;
25  import java.util.List;
26  import java.util.Map;
27  
28  /**
29   * @author jiangxin
30   * @author 2019-04-12
31   *
32   */
33  public class I18nAddPanel extends EasyPanel {
34      
35      @Serial
36      private static final long serialVersionUID = 1L;
37      
38      private static final String CHARSET = "UTF-8";
39  
40      private static final boolean REMOVE_LAST_LF_OPEN = true;
41  
42      private static final Map<String, String> replace = new HashedMap<>();
43  
44      private JTextField srcTextField;
45  
46      private JTextField targetTextField;
47  
48      private JTextField itemTextField;
49  
50      private int operationCount = 0;
51  
52      static {
53          replace.put("&quot;", "jiangxin001");
54          replace.put("&#160;", "jiangxin002");
55      }
56      
57      public I18nAddPanel() throws HeadlessException {
58          super();
59      }
60  
61      @Override
62      public void initUI() {
63          BoxLayout boxLayout = new BoxLayout(this, BoxLayout.Y_AXIS);
64          setLayout(boxLayout);
65  
66          createSourcePanel();
67          add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER));
68          createTargetPanel();
69          add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER));
70          createItemPanel();
71          add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER));
72          createOperationPanel();
73      }
74  
75      private void createOperationPanel() {
76          JPanel operationPanel = new JPanel();
77          operationPanel.setLayout(new BoxLayout(operationPanel, BoxLayout.X_AXIS));
78          add(operationPanel);
79  
80          JButton addButton = new JButton(bundle.getString("android.i18n.add.title"));
81          addButton.addActionListener(e -> {
82              String srcPath = checkAndGetDirContent(srcTextField, "android.i18n.add.src.dir", "Source directory is invalid");
83              if (srcPath == null) {
84                  return;
85              }
86  
87              String targetPath = checkAndGetDirContent(targetTextField, "android.i18n.add.target.dir", "Target directory is invalid");
88              if (targetPath == null) {
89                  return;
90              }
91  
92              String itemStr = checkAndGetStringContent(itemTextField, "android.i18n.add.items", "Items is empty");
93              if (itemStr == null) {
94                  return;
95              }
96  
97              List<String> items = new ArrayList<>(Arrays.asList(itemStr.split(";")));
98              operationCount = 0;
99  
100             for (String item : items) {
101                 int ret = innerProcessor(srcPath, targetPath, item);
102                 if (ret != 0) {
103                     Toolkit.getDefaultToolkit().beep();
104                     JOptionPane.showMessageDialog(this, "Failed, please see the log", "ERROR",
105                             JOptionPane.ERROR_MESSAGE);
106                     return;
107                 }
108             }
109             String message = String.format("Success: items: %d", operationCount);
110             JOptionPane.showMessageDialog(this, message, "INFO", JOptionPane.INFORMATION_MESSAGE);
111         });
112 
113         operationPanel.add(addButton);
114     }
115 
116     private void createItemPanel() {
117         JPanel itemPanel = new JPanel();
118         itemPanel.setLayout(new BoxLayout(itemPanel, BoxLayout.X_AXIS));
119         add(itemPanel);
120         
121         itemTextField = new JTextField();
122         itemTextField.setText(conf.getString("android.i18n.add.items"));
123 
124         JLabel itemLabel = new JLabel("Items");
125 
126         itemPanel.add(itemTextField);
127         itemPanel.add(Box.createHorizontalGlue());
128         itemPanel.add(itemLabel);
129     }
130 
131     private void createTargetPanel() {
132         JPanel targetPanel = new JPanel();
133         targetPanel.setLayout(new BoxLayout(targetPanel, BoxLayout.X_AXIS));
134         add(targetPanel);
135         
136         targetTextField = new JTextField();
137         targetTextField.setText(conf.getString("android.i18n.add.target.dir"));
138 
139         JButton targetButton = new JButton("Save Directory");
140         targetButton.addActionListener(new SelectDirectoryListener("save to", targetTextField));
141 
142         targetPanel.add(targetTextField);
143         targetPanel.add(Box.createHorizontalGlue());
144         targetPanel.add(targetButton);
145     }
146 
147     private void createSourcePanel() {
148         JPanel sourcePanel = new JPanel();
149         sourcePanel.setLayout(new BoxLayout(sourcePanel, BoxLayout.X_AXIS));
150         add(sourcePanel);
151         
152         srcTextField = new JTextField();
153         srcTextField.setText(conf.getString("android.i18n.add.src.dir"));
154 
155         JButton srcButton = new JButton("Source Directory");
156         srcButton.addActionListener(new SelectDirectoryListener("select a directory", srcTextField));
157 
158         sourcePanel.add(srcTextField);
159         sourcePanel.add(Box.createHorizontalGlue());
160         sourcePanel.add(srcButton);
161     }
162 
163     private int innerProcessor(String sourceBaseStr, String targetBaseStr, String itemName) {
164         if (StringUtils.isAnyEmpty(sourceBaseStr, targetBaseStr, itemName)) {
165             logger.error("params are invalid: sourceBaseStr: {}, targetBaseStr: {}, itemName: {}", sourceBaseStr, targetBaseStr, itemName);
166             return -1;
167         }
168         File sourceBaseFile = new File(sourceBaseStr);
169         File targetBaseFile = new File(targetBaseStr);
170         int count = 0;
171 
172         File[] sourceParentFiles = sourceBaseFile.listFiles(new FileFilter() {
173             @Override
174             public boolean accept(File pathname) {
175                 return pathname.getName().startsWith("values");
176             }
177         });
178         if (sourceParentFiles == null) {
179             logger.error("sourceParentFiles is null");
180             return -1;
181         }
182         for (File sourceParentFile : sourceParentFiles) {
183             File sourceFile = new File(sourceParentFile, "strings.xml");
184 
185             Element sourceElement = getSourceElement(sourceFile, itemName);
186             if (sourceElement == null) {
187                 logger.warn("sourceElement is null: {}", sourceFile);
188                 continue;
189             }
190 
191             File targetFile = new File(new File(targetBaseFile, sourceParentFile.getName()), "strings.xml");
192             if (!targetFile.exists()) {
193                 logger.warn("targetFile does not exist: {}", sourceFile);
194                 continue;
195             }
196             try {
197                 preProcess(targetFile);
198             } catch (IOException e) {
199                 logger.error("preProcess failed.", e);
200                 return -1;
201             }
202             boolean res = setTargetElement(targetFile, sourceElement, itemName);
203             if (!res) {
204                 logger.error("setTargetElement failed.");
205                 return -1;
206             }
207             try {
208                 postProcess(targetFile);
209             } catch (IOException e) {
210                 logger.error("postProcess failed.", e);
211                 return -1;
212             }
213             logger.info("count: {}, in path: {}, out path: {}", ++count, sourceFile, targetFile);
214         }
215         operationCount += count;
216         logger.info("finish one cycle");
217         return 0;
218     }
219 
220     private Element getSourceElement(File sourceFile, String itemName) {
221         if (!sourceFile.exists()) {
222             logger.warn("sourceFile does not exist: {}", sourceFile);
223             return null;
224         }
225         SAXBuilder builder = new SAXBuilder();
226         Document sourceDoc = null;
227         try (InputStream in = new FileInputStream(sourceFile)) {
228             sourceDoc = builder.build(in);
229             logger.info("build source document: {}", sourceFile);
230         } catch (JDOMException | IOException e) {
231             logger.error("build source document failed: {}", sourceFile);
232             return null;
233         }
234         if (sourceDoc == null) {
235             logger.error("sourceDoc is null");
236             return null;
237         }
238         Element sourceElement = null;
239         for (Element sourceChild : sourceDoc.getRootElement().getChildren()) {
240             String sourceValue = sourceChild.getAttributeValue("name");
241             if (sourceValue != null && sourceValue.equals(itemName)) {
242                 sourceElement = sourceChild.clone();
243                 break;
244             }
245         }
246         return sourceElement;
247     }
248 
249     private boolean setTargetElement(File targetFile, Element sourceElement, String itemName) {
250         SAXBuilder builder = new SAXBuilder();
251         Document targetDoc;
252         try {
253             targetDoc = builder.build(targetFile);
254             logger.info("build target document: {}", targetFile);
255         } catch (JDOMException | IOException e) {
256             logger.error("build target document failed: {}", targetFile);
257             return false;
258         }
259         Element targetRoot = targetDoc.getRootElement();
260         boolean isFinished = false;
261         for (Element targetChild : targetRoot.getChildren()) {
262             String targetValue = targetChild.getAttributeValue("name");
263             if (targetValue != null && targetValue.equals(itemName)) {
264                 targetChild.setText(sourceElement.getText());
265                 isFinished = true;
266                 break;
267             }
268         }
269         if (!isFinished) {
270             targetRoot.addContent("    ");
271             targetRoot.addContent(sourceElement);
272             targetRoot.addContent("\n");
273         }
274         XMLOutputter out = new XMLOutputter();
275         Format format = Format.getRawFormat();
276         format.setEncoding("UTF-8");
277         format.setLineSeparator("\n");
278         out.setFormat(format);
279         OutputStream os = null;
280         try {
281             os = new FileOutputStream(targetFile);
282             out.output(targetDoc, os);
283         } catch (IOException e) {
284             logger.error("output fail", e);
285             return false;
286         } finally {
287             if (os != null) {
288                 try {
289                     os.close();
290                 } catch (IOException e) {
291                     logger.error("close output stream exception", e);
292                 }
293             }
294         }
295         return true;
296     }
297 
298     private static void preProcess(File file) throws IOException {
299         String content = FileUtils.readFileToString(file, CHARSET);
300         for (Map.Entry<String, String> entry : replace.entrySet()) {
301             content = content.replaceAll(entry.getKey(), entry.getValue());
302         }
303         FileUtils.writeStringToFile(file, content, CHARSET);
304     }
305 
306     private static void postProcess(File file) throws IOException {
307         String content = FileUtils.readFileToString(file, CHARSET);
308         for (Map.Entry<String, String> entry : replace.entrySet()) {
309             content = content.replaceAll(entry.getValue(), entry.getKey());
310         }
311         if (REMOVE_LAST_LF_OPEN) {
312             content = Strings.CS.removeEnd(content, "\n");
313         }
314         FileUtils.writeStringToFile(file, content, CHARSET);
315     }
316 
317 }